package org.jugile.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

/*

Copyright (C) 2013-2013 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

/**
 * "All things were made by him; and without him was not any thing made that was made.
 *  In him was life; and the life was the light of men. And the light shineth in darkness; 
 *  and the darkness comprehended it not." (John 1:3-5)
 * 
 * ===========
 * Convinience wrapper around JSCH.
 * This file needs jsch-0.1.50.jar or newer version to run.
 * @author rahkojuk
 *
 */
public class Ssh extends Jugile {

	public static void scp(String fname, String hostname, String userid, String passwd, String dir) {
		FileInputStream fis=null;
		try{
			String lfile = fname;
			String user = userid;
			String host = hostname;
			int lidx = fname.lastIndexOf('/');
			if (lidx > 0) fname = fname.substring(lidx);
			String rfile= dir + "/" + fname;

			JSch jsch=new JSch();
			Session session=jsch.getSession(user, host, 22);
			session.setConfig("StrictHostKeyChecking", "no"); 
			session.setPassword(passwd);
			session.connect();

			boolean ptimestamp = true;

			// exec 'scp -t rfile' remotely
			String command="scp " + (ptimestamp ? "-p" :"") +" -t "+rfile;
			Channel channel=session.openChannel("exec");
			((ChannelExec)channel).setCommand(command);

			// get I/O streams for remote scp
			OutputStream out=channel.getOutputStream();
			InputStream in=channel.getInputStream();

			channel.connect();

			if(checkAck(in)!=0){
				return;
			}

			File _lfile = new File(lfile);

			if(ptimestamp){
				command="T "+(_lfile.lastModified()/1000)+" 0";
				// The access time should be sent here,
				// but it is not accessible with JavaAPI ;-<
				command+=(" "+(_lfile.lastModified()/1000)+" 0\n"); 
				out.write(command.getBytes()); out.flush();
				if(checkAck(in)!=0){
					return;
				}
			}

			// send "C0644 filesize filename", where filename should not include '/'
			long filesize=_lfile.length();
			command="C0644 "+filesize+" ";
			if(lfile.lastIndexOf('/')>0){
				command+=lfile.substring(lfile.lastIndexOf('/')+1);
			} else {
				command+=lfile;
			}
			command+="\n";
			out.write(command.getBytes()); out.flush();
			if(checkAck(in)!=0){
				return;
			}

			// send a content of lfile
			fis=new FileInputStream(lfile);
			byte[] buf=new byte[1024];
			while(true){
				int len=fis.read(buf, 0, buf.length);
				if(len<=0) break;
				out.write(buf, 0, len); //out.flush();
			}
			fis.close();
			fis=null;
			// send '\0'
			buf[0]=0; out.write(buf, 0, 1); out.flush();
			if(checkAck(in)!=0){
				return;
			}
			out.close();

			channel.disconnect();
			session.disconnect();

			return;
		} catch(Exception e){
			System.out.println(e);
			try{if(fis!=null)fis.close();}catch(Exception ee){}
		}
	}

	public static String sshExec(String cmd, String hostname, String userid, String passwd) {
		try{
			JSch jsch = new JSch();  
			String host = hostname;
			String user = userid;
			Session session = jsch.getSession(user, host, 22);
			session.setConfig("StrictHostKeyChecking", "no"); 
			session.setPassword(passwd);
			session.connect();

			String command= cmd;

			Channel channel=session.openChannel("exec");
			((ChannelExec)channel).setCommand(command);

			channel.setInputStream(null);
			((ChannelExec)channel).setErrStream(System.err);
			InputStream in=channel.getInputStream();

			channel.connect();

			StringBuffer res = new StringBuffer();
			byte[] tmp=new byte[1024];
			while(true){
				while(in.available()>0){
					int i=in.read(tmp, 0, 1024);
					if(i<0)break;
					res.append(new String(tmp, 0, i));
				}
				if(channel.isClosed()){
					res.append("exit-status: "+channel.getExitStatus());
					break;
				}
				try{Thread.sleep(1000);}catch(Exception ee){}
			}
			channel.disconnect();
			session.disconnect();
			return res.toString();
		} catch(Exception e) { fail(e); return null; }
	}
	
	
	static int checkAck(InputStream in) throws IOException {
		int b=in.read();
		// b may be 0 for success,
		//          1 for error,
		//          2 for fatal error,
		//          -1
		if(b==0) return b;
		if(b==-1) return b;
		if(b==1 || b==2){
			StringBuffer sb=new StringBuffer();
			int c;
			do {
				c=in.read();
				sb.append((char)c);
			} while(c!='\n');
			if(b==1){ // error
				System.out.print(sb.toString());
			}
			if(b==2){ // fatal error
				System.out.print(sb.toString());
			}
		}
		return b;
	}
	

}
